home *** CD-ROM | disk | FTP | other *** search
- unit GS_DBNtx;
- {-----------------------------------------------------------------------------
- Clipper Index Handler
-
- GS_DBNtx Copyright (c) Richard F. Griffin
-
- 4 August 1991
-
- 102 Molded Stone Pl
- Warner Robins, GA 31088
-
- -------------------------------------------------------------
- This unit handles the objects for all Clipper index (.NTX)
- operations. This unit may be implemented by changing the
- GS_DBASE.PAS unit's USES statement from GS_DBNDX to GS_DBNTX.
- That's the only change necessary to replace .NDX indexes with
- Clipper .NTX indexes.
-
- changes:
-
- 02 Feb 92 - Added call to KeyLocRec in main part of KeyUpdate.
- This allows multiple indexes to be used. In the past,
- the program assumed the index was pointing to the
- current record. There is a sacrifice in update
- speed, however.
-
- 18 Feb 92 - Fixed numerous problems with KeyFind and KeyUpdate.
- Corrected problem to ensure the first duplicate key
- is retrieved. Corrected Index key insertion problem.
-
- Added KeyBOF flag for test for access beyond top of
- file.
-
- 19 Feb 92 - Embedded cache into Ndx_Get and Ndx_Put. A number
- of node images will be stored to memory. This will
- be treated as a stack, where the last page accessed
- will be pushed to the top and new nodes will use the
- bottom image. They will replace the old image and
- push to the top. This allows the most active nodes to
- remain in memory, with less active nodes being swapped
- out. This also added a Ndx_Flush method to write all
- updated nodes to disk on demand, such as at closing.
-
- ------------------------------------------------------------------------------}
-
- interface
- {$D-}
-
- uses
- GS_Strng, {String handler routines}
- GS_Error, {Error handler routines}
- GS_FileH; {File handler routines}
-
- const
- NdxBufSize = 4096;
- IndexSignature = 'NTX';
- NdxBufferedPages = 16;
-
- type
-
- {
- ┌──────────────────────────────────────────────────────────┐
- │ ******** Index Header Description ******** │
- └──────────────────────────────────────────────────────────┘
- }
- GS_Indx_Head = Record
- Vers1,
- Vers2 : Integer;
- Root : Longint;
- Unknwn1 : Longint;
- Entry_Sz : Integer;
- Key_Lgth : Integer;
- Unknwn2 : Integer;
- Max_Keys : Integer;
- Min_Keys : Integer;
- Key_Form : array [0..1001] of char;
- end;
-
- {
- ┌──────────────────────────────────────────────────────────┐
- │ ******** Index Node Header Description ******** │
- └──────────────────────────────────────────────────────────┘
- }
-
- GS_Indx_Data = Record {300 additional bytes for overflow}
- case integer of
- 0 : (Data_Ary : array [0..1323] of byte);
- 1 : (Indx_Ary : array [0..661] of word);
- 2 : (Entry_Ct : Integer);
- end;
-
- GS_Indx_EntPtr = ^GS_Indx_Etry;
-
-
- {
- ┌──────────────────────────────────────────────────────────┐
- │ ******** Index Node Key Entry Description ******* │
- └──────────────────────────────────────────────────────────┘
- }
-
- GS_Indx_Etry = Record
- Block_Ax : Longint;
- Recrd_Ax : Longint;
- Char_Fld : array [1..255] of char;
- end;
-
- GS_Indx_Tabl = Record
- Page_No : Longint; {Disk block holding node info}
- Etry_No : Longint; {Last entry used in node}
- Last_One : Longint; {Number of keys in this node }
- Node_Pag : Boolean; {True for non-leaf nodes}
- end;
-
- GS_Indx_LPtr = ^GS_dBase_IX; {Pointer to object. Used by GS_dBase_DB}
-
- GS_DiskPagPtr = ^GS_DiskPagBfr;
- GS_DiskPagBfr = array[0..1023] of byte;
-
- GS_DiskTblPtr = ^GS_DiskTblPag;
- GS_DiskTblPag = record
- BlkNum : longint;
- BlkWrt : boolean;
- BlkPtr : GS_DiskPagPtr;
- end;
-
- {
- ┌─────────────────────────────────┐
- │ GS_dBase_IX Object Definition │
- └─────────────────────────────────┘
- }
-
- GS_dBase_IX = object
- Ndx_Name : String[64]; {File name of index file}
- Ndx_Hdr : GS_Indx_Head; {Index header information}
- Ndx_File : file; {File type for index file}
- Ndx_Tabl : array [0..25] of GS_Indx_Tabl;
- {Array of 25 table entries to hold}
- {the trail of non-leaf nodes that are}
- {traversed during a key search. This }
- {table is needed to track positions for}
- {sequential reads (next and previous).}
-
- Ndx_Lvl : integer; {Holds counter into Ndx_Tabl}
- Ndx_Data : GS_Indx_Data; {Node header information}
- Ndx_Pntr : GS_Indx_EntPtr; {Pointer to key entry information}
- Ndx_Key_St : string[255]; {Holds last key value found on call to}
- {either KeyRead or KeyFind}
-
- Ndx_Key_Num : longint; {Holds last physical record number for a}
- {key value found on call to either}
- {KeyRead or KeyFind}
- Ndx_Key_Form : string[127]; {Holds the key formula in type string}
- KeyBOF : boolean;
- KeyEOF : boolean; {True if last KeyRead attempted to read}
- {beyond the range of index keys - either}
- {beyond beginning or end of file}
- ExactMatch : boolean; {Flag for type of test to use in KeyFind}
- {It will force a match against an entire}
- {key if true, and only for the length of}
- {the passed argument if false. It is}
- {initialized true.}
-
- Ndx_PagArray : array[0..NdxBufferedPages-1] of GS_DiskTblPag;
-
- CONSTRUCTOR Init(IName : String);
- CONSTRUCTOR Ndx_Make(filname,formla: string;lth: integer;typ: char);
- DESTRUCTOR Done;
- FUNCTION KeyFind(st : String) : longint;
- FUNCTION KeyLocRec(rec : longint) : boolean;
- FUNCTION KeyRead(a : LongInt) : longint;
- PROCEDURE KeyUpdate (st : string; rec, crec : longint);
- PROCEDURE Ndx_Close;
- Procedure Ndx_Flush;
- PROCEDURE Ndx_Get(blk : longint);
- PROCEDURE Ndx_GetRecEntry;
- PROCEDURE Ndx_GetRecPage(Ascnd : boolean);
- FUNCTION Ndx_LastEntry : boolean;
- PROCEDURE Ndx_NodeData(pn, en, lo : longint; np : boolean);
- PROCEDURE Ndx_Put(blk : longint);
- Procedure KeyList(st : string);
- FUNCTION SetMatchValue(st : string): string;
-
-
-
- end;
- {.pa}
- {
- ┌──────────────────────────┐
- │ IMPLEMENTATION SECTION │
- └──────────────────────────┘
- }
-
- implementation
-
-
- const
- Node_Size = 1024; {Size of the node}
-
- Next_Record = -1; {Token value passed to read next record}
- Prev_Record = -2; {Token value passed to read previous record}
- Top_Record = -3; {Token value passed to read first record}
- Bttm_Record = -4; {Token value passed to read final record}
-
- ValueHigh = 1; {Token value passed for key comparison high}
- ValueLow = -1; {Token value passed for key comparison low}
- ValueEqual = 0; {Token value passed for key comparison equal}
-
- var
- Work_Key : string; {Holds key passed in Find and KeyUpdate}
- Work_Num : Double; {Holds numeric value of Work_Key if needed}
- RPag : Longint; {Work variable to hold current index block}
- RNum : Longint; {Work variable for record number}
- IsAscend : Boolean; {Flag for ascending/descending status.}
- {Set based on Next/Previous Record read}
-
-
- Constructor GS_dBase_IX.Init(IName : String);
- var
- i : integer;
- begin
- for i := 0 to NdxBufferedPages-1 do
- begin
- Ndx_PagArray[i].BlkNum := -1;
- Ndx_PagArray[i].BlkWrt := false;
- Ndx_PagArray[i].BlkPtr := nil;
- end;
-
- Ndx_Name := IName + '.NTX';
- if GS_FileExists(Ndx_File, Ndx_Name) then
- begin
- GS_FileAssign(Ndx_File,Ndx_Name);
- GS_FileReset(Ndx_File,1);
- end
- else
- begin
- ShowError(2,Ndx_Name);
- end;
- Ndx_Get(0); {Read first block of file for header info}
- {Note that no error checking is done }
- {in this version }
- move(Ndx_Data, Ndx_Hdr, Node_Size);
- {Store in header info area}
- Ndx_Lvl := 0; {Initialize the node step table}
- Ndx_Tabl[0].Page_No := 0;
- Ndx_Tabl[0].Etry_No := 0;
- Ndx_Tabl[0].Last_One := 0;
- KeyEOF := false; {Initialize EOF Flag to false}
- ExactMatch := true; {Initialize to use an exact match test}
- move(Ndx_Hdr.Key_Form[0], Ndx_Key_Form[1],100);
- i := 1;
- while Ndx_Key_Form[i] <> #0 do inc(i);
- Ndx_Key_Form[0] := chr(pred(i));
- Ndx_Key_Form := TrimR(Ndx_Key_Form);
- Ndx_Key_Form := TrimL(Ndx_Key_Form);
- end;
-
-
- Destructor GS_dBase_IX.Done;
- var
- i : integer;
- begin
- Ndx_Close;
- for i := 0 to NdxBufferedPages-1 do
- if Ndx_PagArray[i].BlkPtr <> nil then Dispose(Ndx_PagArray[i].BlkPtr);
- end;
-
-
- Constructor GS_dBase_IX.Ndx_Make(filname, formla : string; lth : integer;
- typ : char);
- var
- i : integer;
- begin
- for i := 0 to NdxBufferedPages-1 do
- begin
- Ndx_PagArray[i].BlkNum := -1;
- Ndx_PagArray[i].BlkWrt := false;
- Ndx_PagArray[i].BlkPtr := nil;
- end;
-
- Ndx_Name := filname+'.NTX'; {Setup file name}
- GS_FileAssign(Ndx_File,Ndx_Name);
- GS_FileRewrite(Ndx_File,1);
- FillChar(Ndx_Hdr, SizeOf(Ndx_Hdr),#0);
- Ndx_Hdr.Vers1 := 6;
- Ndx_Hdr.Vers2 := 1;
- Ndx_Hdr.Root := Node_Size;
- Ndx_Hdr.Key_Lgth := lth;
- Ndx_Hdr.Max_Keys := ((SizeOf(Ndx_Hdr)-4) div (lth+10)) - 1;
- Ndx_Hdr.Min_Keys := Ndx_Hdr.Max_Keys div 2;
- Ndx_Hdr.Entry_Sz := lth+8;
- CnvStrToAsc(formla,Ndx_Hdr.Key_Form, length(formla)+1);
- move(Ndx_Hdr, Ndx_Data, SizeOf(Ndx_Hdr));
- Ndx_Put(0);
- FillChar(Ndx_Data, SizeOf(Ndx_Data),#0);
- for i := 0 to Ndx_Hdr.Max_Keys do Ndx_Data.Indx_Ary[succ(i)] :=
- ((Ndx_Hdr.Max_Keys + 2) * 2) + (Ndx_Hdr.Entry_Sz * i);
- Ndx_Put(1);
- end;
-
- function GS_dBase_IX.SetMatchValue(st : string): string;
- var
- rl : integer;
- begin
- FillChar(Work_Key[1], SizeOf(Work_Key), ' '); {Fill with blanks}
- Work_Key := st;
- if ExactMatch then
- Work_Key[0] := chr(Ndx_Hdr.Key_Lgth);
- SetMatchValue := Work_Key;
- end;
-
- {.pa}
- {
- KEYFIND
-
-
- ╔══════════════════════════════════════════════════════════════════╗
- ║ ║
- ║ The KeyFind method will return the physical record location ║
- ║ of the record matching the key value passed as the argument. ║
- ║ ExactMatch controls the length of the match check. If ║
- ║ ExactMatch is true, the entire key in the .NDX entry must ║
- ║ match the value passed. If false, the check will only be ║
- ║ for the length of the string passed. ║
- ║ ║
- ║ Calling the Method: ║
- ║ ║
- ║ longintvalu := objectname.KeyFind(string) ║
- ║ ║
- ║ ( where objectname is of type GS_dBase_IX, ║
- ║ string is a value used to search the ║
- ║ .NDX file looking for a match. ║
- ║ ║
- ║ Result: ║
- ║ ║
- ║ 1. longintvalu will point to the physical record, ║
- ║ or will be zero if no match. ║
- ║ 2. Ndx_Key_St will contain the key value. ║
- ║ 3. Ndx_Key_Num will contain the record number. ║
- ║ ║
- ╚══════════════════════════════════════════════════════════════════╝
- }
-
-
- function GS_dBase_IX.KeyFind(st : string) : LongInt;
- var
- i : integer; {Work variable}
- rl : integer; {Result code for Val procedure}
- ct : integer; {Variable to hold BlockRead byte count}
- Less_Than : boolean; {Flag to hunt for key match}
- Loop_Cnt : longint;
- Match_Cnd : integer;
- NL_Match : Longint;
-
- procedure StoreMatchValue;
- begin
- move(Ndx_Pntr^.Char_Fld,Ndx_Key_St[1],Ndx_Hdr.Key_Lgth);
- {Move the key field to Ndx_Key_St.}
- Ndx_Key_St[0] := Work_Key[0]; {Now insert the length into Ndx_Key_St}
- end;
-
- function DoMatchValue : integer;
- begin
- if Ndx_Key_St > Work_Key then Match_Cnd := ValueHigh
- else if Ndx_Key_St = Work_Key then Match_Cnd := ValueEqual
- else Match_Cnd := ValueLow;
- DoMatchValue := Match_Cnd;
- end;
-
- begin
- KeyEOF := false; {Reset End-of-File to false}
- Ndx_Key_Num := 0; {Initialize}
- Ndx_Key_St := ''; {Initialize}
- Ndx_Lvl := 0; {Initialize index level}
- Match_Cnd := ValueLow;
- NL_Match := 0; {Non-leaf node key match flag}
- Work_Key := SetMatchValue(st); {Set key comparison value}
- RPag := Ndx_Hdr.Root div Node_Size;
- {Get root node address}
- while (RPag <> 0) do
- {While a non-leaf node, do this}
- begin
- Ndx_Get(RPag); {Get Node using RPag as block number}
- Ndx_Pntr := Addr(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[1]]);
- {Get pointer to first entry}
- Loop_Cnt := Ndx_Pntr^.Block_Ax div Node_Size;
- {Get the next node pointer to see if it}
- {is zero, meaning a leaf node}
- i := 0; {Initialize i as counter}
- Less_Than := Ndx_Data.Entry_Ct > 0;
- {Start out with less than flag true}
- {Will be false if Entry Count is 0}
- {which means an empty node}
- while (less_than) and (i <= Ndx_Data.Entry_Ct) do
- {Hunt for a match. If i = last entry in}
- {the node, the last entry is used for}
- {the next node search}
- begin
- Ndx_Pntr := Addr(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[i+1]]);
- {Get pointer to entry indexed by i}
-
- inc(i); {Increment the counter}
- StoreMatchValue; {Put the key value in Ndx_Key_St for}
- {matching}
-
- Less_Than := DoMatchValue = ValueLow;
- {Test looking for greater or equal than}
- {the key value. Less_Than will be set}
- {false when found, setting the condition}
- {to leave this portion of the routine}
-
- if Match_Cnd = ValueEqual then
- NL_Match := Ndx_Pntr^.Recrd_Ax;
- end;
- {
- ┌──────────────────────────────────────────┐
- │ Save the node data for this node as: │
- │ 1. Block Number from RPag. │
- │ 2. Entry number of match or last one. │
- │ 3. Set total number of entries. This │
- │ is entry count+1 for non-leaf nodes │
- │ 4. Set non-leaf flag to true. │
- └──────────────────────────────────────────┘
- }
- Ndx_NodeData(RPag,i,Ndx_Data.Entry_Ct+1,true);
- if Loop_Cnt = 0 then RPag := 0
- else RPag := Ndx_Pntr^.Block_Ax div Node_Size;
- {Get the next node in the tree}
- end;
- if RPag = 0 then
- begin
- Ndx_Tabl[Ndx_Lvl].Node_Pag := false;
- {Set non-leaf flag to false for this}
- {last level}
- dec(Ndx_Tabl[Ndx_Lvl].Last_One);
- {Set total number of entries to the }
- {correct value for a leaf node}
- end;
-
- if Ndx_Data.Entry_Ct = 0 then
- begin
- KeyFind := 0;
- exit;
- end;
-
- if (Match_Cnd <> ValueEqual) or
- (Ndx_Tabl[Ndx_Lvl].Last_One < Ndx_Tabl[Ndx_Lvl].Etry_No)
- then Ndx_Key_Num := 0 {if unable to find a match, the above}
- {routine would have stopped when a}
- {greater key was found, or would have}
- {continued to Last_One. Since the entry}
- {count is one less for leaf nodes, even}
- {if there was a match at Last_one, it is}
- {not valid, and was only a coincidence.}
- {In either case, set record number = 0.}
- else
- Ndx_Key_Num := Ndx_Pntr^.Recrd_Ax;
- {When there is a match with the key,}
- {get the physical record number}
-
- if (Ndx_Key_Num = 0) and (NL_Match > 0) then
- begin {set if match in non-leaf node}
- Ndx_Key_St := Work_Key;
- Ndx_Key_Num := NL_Match;
- dec(Ndx_Lvl);
- end;
-
- KeyFind := Ndx_Key_Num; {Return with the record number}
- end;
- {.pa}
- {
- KEYLOCREC
-
-
- ╔══════════════════════════════════════════════════════════════════╗
- ║ ║
- ║ The KeyLocRec method will search the .NDX file to find the ║
- ║ matching index entry pointing to the physical record location ║
- ║ of the record requested. ║
- ║ ║
- ║ Calling the Method: ║
- ║ ║
- ║ flag := objectname.KeyLocRec(key, position) ║
- ║ ║
- ║ ( where objectname is of type GS_dBase_IX, ║
- ║ key is the key string ║
- ║ position is the physical record number ║
- ║ of the matching .DBF record.) ║
- ║ ║
- ║ Result: ║
- ║ ║
- ║ Boolean True is returned if a match is found. ║
- ║ The current index entry will be set to the record ║
- ║ if a match does exist. ║
- ║ ║
- ╚══════════════════════════════════════════════════════════════════╝
- }
-
-
- Function GS_dBase_IX.KeyLocRec (rec : longint) : boolean;
- var
- lr : longint;
- begin
- if rec = Ndx_Key_Num then
- begin {Exit if already at the record}
- KeyLocRec := true;
- exit;
- end;
- lr := KeyRead(Top_Record);
- while (not KeyEOF) and (lr <> rec) do lr := KeyRead(Next_Record);
- if (KeyEOF) then KeyLocRec := false
- else KeyLocRec := true;
- end;
- {.pa}
- {
- KEYREAD
-
-
- ╔══════════════════════════════════════════════════════════════════╗
- ║ ║
- ║ The KeyRead method will return the physical record location ║
- ║ of the record requested. The only options that may be asked ║
- ║ for are Top, Bottom, Next, and Previous. ║
- ║ ║
- ║ Calling the Method: ║
- ║ ║
- ║ longintvalu := objectname.KeyRead(position) ║
- ║ ║
- ║ ( where objectname is of type GS_dBase_IX, ║
- ║ position is in -1 to -4, ║
- ║ longintvalu is physical record number ║
- ║ of the matching .DBF record. ║
- ║ ║
- ║ Result: ║
- ║ ║
- ║ longintvalu will point to the physical record. ║
- ║ ║
- ╚══════════════════════════════════════════════════════════════════╝
- }
-
-
- FUNCTION GS_dBase_IX.KeyRead(a : longint) : longint;
- var
- N_L_Hold : Integer; {Tempory variable for index level}
- ct : Integer; {Work variable for Blockread count}
-
-
-
- {
- ┌───────────────────────────────────────────────┐
- │ Start of KeyRead function. This will │
- │ accomplish the following: │
- │ │
- │ 1. If first time for index, set any call │
- │ for a Next or Previous read to a Top │
- │ read command. │
- │ 2. Use case select for Top/Bttm/Next/Prev. │
- │ Return physical .DBF record in RNum. │
- │ 3. If not a valid action, set RNum to 0. │
- │ 4. Move key value to Ndx_Key_St. │
- │ 5. Move RNum to Ndx_Key_Num. │
- │ 6. Return RNum value to calling procedure. │
- └───────────────────────────────────────────────┘
- }
-
-
- { Start of KeyRead }
-
- begin
- RNum := a; {Get action command}
- if ((a = Next_Record) or (a = Prev_Record)) and
- (Ndx_Lvl = 0) then RNum := Top_Record;
- {if first time through, use Top_Record}
- {command instead}
- KeyBOF := false;
- KeyEOF := false; {End-of-File initially set false}
- case RNum of {Select KeyRead Action}
-
- Next_Record : begin
- IsAscend := true;
- {Will be an ascending read}
- N_L_Hold := Ndx_Lvl;
- {Save old index level}
- {
- ┌─────────────────────────────────────┐
- │ If the last record read was the │
- │ last entry in the node, you have │
- │ to step back through the index │
- │ levels to find the next node. │
- └─────────────────────────────────────┘
- }
- if Ndx_LastEntry then
- {If last entry in node already used,}
- {go find the next node}
- begin
- while (Ndx_LastEntry) and (Ndx_Lvl > 0) do
- dec(Ndx_Lvl);
- {Step back through the levels until you}
- {find a good one, or run out of levels.}
-
- if Ndx_Lvl = 0 then
- {if out of levels, process for EOF}
- begin
- Ndx_Lvl := N_L_Hold;
- {Get old level number to restore}
- KeyEOF := true;
- {Set End-of-File true}
- end else
-
- begin {Otherwise, get non-leaf next entry data}
- RPag := Ndx_Tabl[Ndx_Lvl].Page_No;
- Ndx_Get(RPag);
- end;
- end
- else
- begin
- if Ndx_Tabl[Ndx_Lvl].Node_Pag then
- begin {this a non-leaf node}
- inc(Ndx_Tabl[Ndx_Lvl].Etry_No);
- {Step to next Entry Number}
- Ndx_GetRecEntry;
- {Go search for next good record}
- end
- else inc(Ndx_Tabl[Ndx_Lvl].Etry_No);
- {Otherwise, just step to next entry}
- end;
- Ndx_Pntr := Addr(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary
- [Ndx_Tabl[Ndx_Lvl].Etry_No]]);
- {Get pointer to the key entry}
- RNum := Ndx_Pntr^.Recrd_Ax;
- {Get record number for the key entry}
- end;
-
- Prev_Record : begin
- IsAscend := false;
- {Will be a descending read}
- N_L_Hold := Ndx_Lvl;
- {Save old index level}
- {
- ┌─────────────────────────────────────┐
- │ If the last record read was the │
- │ first entry in the node, you have │
- │ to step back through the index │
- │ levels to find the next node. │
- └─────────────────────────────────────┘
- }
- if Ndx_Tabl[Ndx_Lvl].Node_Pag then
- begin {this a non-leaf node}
- Ndx_GetRecEntry;
- {Go search for next good record}
- end
- else
- begin
- if Ndx_Tabl[Ndx_Lvl].Etry_No = 1 then
- {If last entry in node already used,}
- {go find the next node}
- begin
- while (Ndx_Tabl[Ndx_Lvl].Etry_No = 1) and
- (Ndx_Lvl > 0) do dec(Ndx_Lvl);
- {Step back through the levels until you}
- {find a good one, or run out of levels.}
-
- if Ndx_Lvl = 0 then
- {if out of levels, process for EOF}
- begin
- Ndx_Lvl := N_L_Hold;
- {Get old level number to restore}
- KeyBOF := true;
- {Set Top-of-File true}
- end else
-
- begin {Otherwise, get next entry data}
- dec(Ndx_Tabl[Ndx_Lvl].Etry_No);
- {Step to next Entry Number}
- RPag := Ndx_Tabl[Ndx_Lvl].Page_No;
- Ndx_Get(RPag);
- {Go search for next good record}
- end;
- end
- else dec(Ndx_Tabl[Ndx_Lvl].Etry_No);
- {Otherwise, just step to next entry}
- end;
- Ndx_Pntr := Addr(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary
- [Ndx_Tabl[Ndx_Lvl].Etry_No]]);
- {Get pointer to the key entry}
- RNum := Ndx_Pntr^.Recrd_Ax;
- {Get record number for the key entry}
- end;
-
- Top_Record,
- Bttm_Record : begin
- IsAscend := Top_Record = RNum;
- {Ascending search if Top, otherwise}
- {descending. An ascending search will}
- {return the first index key as the Top.}
- {A descending search will return the}
- {last index key as the 'Top'}
- Ndx_Lvl := 0; {Clear index levels for new stack}
- RPag := Ndx_Hdr.Root div Node_Size;
- {Get root node address}
- Ndx_GetRecPage(IsAscend);
- {Go get valid record}
- end;
-
- else RNum := 0; {If no valid action, return zero}
- end;
- move(Ndx_Pntr^.Char_Fld,Ndx_Key_St[1],Ndx_Hdr.Key_Lgth);
- {Move the key field to Ndx_Key_St.}
- {The Move procedure must be used since}
- {Char_Fld is not a true Pascal string.}
- Ndx_Key_St[0] := chr(Ndx_Hdr.Key_Lgth);
- {Now insert the length into Ndx_Key_St}
- {so it is a valid string we can use}
-
- Ndx_Key_Num := RNum; {Save RNum in Ndx_Key_Num}
- KeyRead := RNum; {Return RNum}
- end;
-
- Procedure GS_dBase_IX.Ndx_Close;
- begin
- Ndx_Flush;
- GS_FileClose(Ndx_File);
- end;
-
-
- Procedure GS_dBase_IX.Ndx_Flush;
- var
- r : word;
- v : integer;
- begin
- for v := 0 to NdxBufferedPages-1 do
- begin
- if v >= 0 then
- begin
- if Ndx_PagArray[v].BlkWrt then
- begin
- GS_FileWrite(Ndx_File,Ndx_PagArray[v].BlkNum*1024,
- Ndx_PagArray[v].BlkPtr^,1024,r);
- if r < 1024 then ShowError(100,'Ndx_Get/Put');
- end;
- Ndx_PagArray[v].BlkWrt := false;
- end;
- end;
- end;
-
- Procedure GS_dBase_IX.Ndx_Get(blk : longint);
- var
- d : GS_DiskTblPag;
- r : word;
- i : integer;
- v : integer;
- begin
- v := -1;
- for i := 0 to NdxBufferedPages-1 do
- if Ndx_PagArray[i].BlkNum = blk then v := i;
- if v < 0 then
- begin
- v := NdxBufferedPages-1;
- if Ndx_PagArray[v].BlkWrt then
- begin
- GS_FileWrite(Ndx_File,Ndx_PagArray[v].BlkNum*1024,
- Ndx_PagArray[v].BlkPtr^,1024,r);
- if r < 1024 then ShowError(100,'Ndx_Get/Put');
- end;
- Ndx_PagArray[v].BlkNum := blk;
- Ndx_PagArray[v].BlkWrt := false;
- if Ndx_PagArray[v].BlkPtr = nil then New(Ndx_PagArray[v].BlkPtr);
- GS_FileRead(Ndx_File,blk*1024,Ndx_PagArray[v].BlkPtr^,1024,r);
- if r < 1024 then
- begin
- ShowError(100,'Ndx_Get');
- end;
- end;
- d := Ndx_PagArray[v];
- if v <> 0 then move(Ndx_PagArray[0],Ndx_PagArray[1],SizeOf(d)*v);
- Ndx_PagArray[0] := d;
- move(d.BlkPtr^,Ndx_Data,1024);
- end;
-
-
-
- Procedure GS_dBase_IX.Ndx_NodeData(pn, en, lo : longint; np : boolean);
- begin
- inc(Ndx_Lvl); {Prepare to store node information as}
- {part of the Ndx_Lvl hierarchy}
- with Ndx_Tabl[Ndx_Lvl] do {Use the index level entry}
- begin
- Page_No := pn; {Save Block number}
- Etry_No := en; {Set entry number}
- Last_One := lo; {Set total number of entries.}
- Node_Pag := np; {Set non-leaf flag}
- end;
- end;
-
-
- procedure GS_dBase_IX.Ndx_GetRecEntry;
- begin
- RPag := Ndx_Tabl[Ndx_Lvl].Page_No;
- {Get page number for this index level}
- Ndx_Get(RPag); {Get Node using RPag as block number}
- Ndx_Pntr := Addr
- (Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[Ndx_Tabl[Ndx_Lvl].Etry_No]]);
- {Get pointer to key entry (relative zero)}
- RPag := Ndx_Pntr^.Block_Ax div Node_Size;
- {Get Next node number in RPag}
- Ndx_GetRecPage(IsAscend); {Go get the next record from a non-leaf}
- {node. Pass the argument for either an}
- {ascending or descending search}
- end;
-
-
- procedure GS_dBase_IX.Ndx_GetRecPage(Ascnd : boolean);
- var
- ec : integer; {Work variable for entry count}
- begin
- while RPag <> 0 do {Next node number in RPag will be zero}
- {when taken from a leaf node.}
- begin
- Ndx_Get(RPag); {Get Node using RPag as block number}
- Ndx_NodeData(RPag,0,Ndx_Data.Entry_Ct+1,true);
- {Store Node data}
- if Ascnd then
- begin
- ec := 0; {Set ec = first entry (relative zero)}
- Ndx_Tabl[Ndx_Lvl].Etry_No := 1;
- {Set Entry Number in table to first one}
- end else
- begin
- ec := Ndx_Data.Entry_Ct; {Set ec to last entry (relative zero)}
- {Note there are Entry_Ct+1 entries for}
- {non-leaf nodes. It will be adjusted}
- {later if it is a leaf node}
- Ndx_Tabl[Ndx_Lvl].Etry_No := ec+1;
- {Set Entry Number in table to last one}
- end;
-
- Ndx_Pntr := Addr(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[ec + 1]]);
- {Get pointer to correct entry in node}
- RPag := Ndx_Pntr^.Block_Ax div Node_Size;
- {Get Next node number in RPag}
- if (not Ascnd) and (Ndx_Pntr^.Recrd_Ax > 0) then RPag := 0;
- {on descend read, process this node}
- end;
- if Ndx_Data.Entry_Ct = 0 then
- begin
- KeyEOF := true;
- RNum := 0;
- exit;
- end;
- Ndx_Tabl[Ndx_Lvl].Node_Pag := Ndx_Pntr^.Block_Ax <> 0;
- {Set non-leaf flag to false for this}
- {if it is the last level}
- if not Ndx_Tabl[Ndx_Lvl].Node_Pag then
- begin {if on a leaf node, adjust}
- if not Ascnd then
- begin
- dec(Ndx_Tabl[Ndx_Lvl].Etry_No);
- {Set Entry Number in table to last one}
- {for a non-leaf node}
- Ndx_Pntr := Addr(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[ec]]);
- {Get pointer to correct leaf entry for}
- {the last entry in the node}
- end;
- dec(Ndx_Tabl[Ndx_Lvl].Last_One);
- {Set total number of entries to the }
- {correct value for a leaf node}
- end;
- RNum := Ndx_Pntr^.Recrd_Ax; {Get the physical record number for}
- {the first key entry}
- end;
-
-
- function GS_dBase_IX.Ndx_LastEntry : boolean;
- begin
- if Ndx_Tabl[Ndx_Lvl].Etry_No = Ndx_Tabl[Ndx_Lvl].Last_One then
- Ndx_LastEntry := true else Ndx_LastEntry := false;
- end;
-
- Procedure GS_dBase_IX.Ndx_Put(blk : longint);
- var
- d : GS_DiskTblPag;
- r : word;
- i : integer;
- v : integer;
- begin
- v := -1;
- for i := 0 to NdxBufferedPages-1 do
- if Ndx_PagArray[i].BlkNum = blk then v := i;
- if v < 0 then
- begin
- v := NdxBufferedPages-1;
- if Ndx_PagArray[v].BlkWrt then
- begin
- GS_FileWrite(Ndx_File,Ndx_PagArray[v].BlkNum*1024,
- Ndx_PagArray[v].BlkPtr^,1024,r);
- if r < 1024 then ShowError(100,'Ndx_Put/Old');
- end;
- Ndx_PagArray[v].BlkNum := blk;
- if Ndx_PagArray[v].BlkPtr = nil then New(Ndx_PagArray[v].BlkPtr);
- GS_FileWrite(Ndx_File,blk*1024,Ndx_Data,1024,r);
- if r < 1024 then ShowError(100,'Ndx_Put/New');
- end;
- d := Ndx_PagArray[v];
- if v <> 0 then move(Ndx_PagArray[0],Ndx_PagArray[1],SizeOf(d)*v);
- d.BlkWrt := true;
- Ndx_PagArray[0] := d;
- move(Ndx_Data,d.BlkPtr^,1024);
- end;
-
- Procedure GS_dBase_IX.KeyUpdate (st : string; rec, crec : longint);
- var
- ForceInsert : boolean;
- ct : integer;
- nu_key : longint;
- em_hold : boolean; {holds ExactMatch flag during this}
- t_num : double;
- lr,
- b1,
- b2,
- pr : longint;
- rlst,
- e1,
- e2,
- n1,
- n2 : integer;
- s1,
- s2 : string[127];
- r1 : GS_Indx_Data;
-
- {
- This routine deletes the current entry by overlaying the remaining entries
- over the entry location, and then decrementing the entry count
- }
- Procedure DeleteEntry;
- begin
- with Ndx_Tabl[Ndx_Lvl] do
- begin
- if Etry_No <= Last_One then
- begin
- move(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[succ(Etry_No)]],
- Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[Etry_No]],
- Ndx_Hdr.Entry_Sz*(Last_One-Etry_No));
- dec(Last_One);
- dec(Ndx_Data.Entry_Ct);
- Ndx_Put(Ndx_Tabl[Ndx_Lvl].Page_No);
- end;
- end;
- end;
-
-
- { This routine inserts an entry by making room in the current data array
- and inserting the new entry. The entry count is then incremented.
- }
- Procedure InsertEntry;
- begin
- with Ndx_Tabl[Ndx_Lvl] do
- begin
- Ndx_Pntr := Addr(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[Last_One]]);
- if (Etry_No <> 0) and (not KeyEOF) and (not ForceInsert) then
- begin {If at a valid entry number and not}
- {at EOF, make room for the entry. }
- move(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[Etry_No]],
- Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[succ(Etry_No)]],
- Ndx_Hdr.Entry_Sz*(((Last_One-Etry_No)+1)));
- Ndx_Pntr := Addr(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[Etry_No]]);
- end
- else
- begin {else put entry at end of array}
- Ndx_Pntr := Addr(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[Etry_No+1]]);
- inc(Etry_No);
- ForceInsert := false;
- end;
- inc(Last_One); {account for additional entry}
- inc(Ndx_Data.Entry_Ct); {account for additional entry}
- move(Work_Key[1],Ndx_Pntr^.Char_Fld,Ndx_Hdr.Key_Lgth)
- {Move the key field from Work_Key.}
- {The Move procedure must be used since}
- {Char_Fld is not a true Pascal string.}
- end;
- end;
-
- { This routine searches back through the nodes to replace the key value in
- the non-leaf node.
- }
- procedure ReplacePointerEntry;
- begin
- while (Ndx_LastEntry) and (Ndx_Lvl > 0) do dec(Ndx_Lvl);
- {Search for entry that requires the key}
- {value. Value is not needed for the }
- {last entry in a non-leaf node. Thus, }
- {this searches until it finds a pointer}
- {that is not the last entry in a node, }
- {or until the root node is reached. }
- if Ndx_Lvl > 0 then
- begin {Replace key value with new one if not }
- {the last entry in the root node. }
- Ndx_Get(Ndx_Tabl[Ndx_Lvl].Page_No);
- {Get the correct index node.}
- Ndx_Pntr := Addr(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary
- [Ndx_Tabl[Ndx_Lvl].Etry_No]]);
- {Get entry that pointed to the leaf node}
- move(Ndx_Key_St[1],Ndx_Pntr^.Char_Fld,Ndx_Hdr.Key_Lgth);
- {Move the key field from Ndx_Key_St.}
- Ndx_Pntr^.Recrd_Ax := pr; {Save the record pointer for the key}
- Ndx_Put(Ndx_Tabl[Ndx_Lvl].Page_No);
- {Write updated node to disk}
- end;
- end;
-
-
- { This routine is used to delete all references to a record key. It will
- delete the key from the leaf node, and then search the non-leaf node and
- replace the pointer if it was the last entry in the non-leaf node.
- }
- Procedure KeyDelete;
- begin
- DeleteEntry; {delete the key from this node.}
- if Ndx_Tabl[Ndx_Lvl].Last_One = 0 then
- begin {if this was the only entry, then }
- {go delete any previous references}
- {to the node. }
- dec(Ndx_Lvl);
- if Ndx_Lvl > 0 then
- begin {this will be recursive until it }
- {steps past the root node. }
- Ndx_Get(Ndx_Tabl[Ndx_Lvl].Page_No);
- {Get the node.}
- KeyDelete; {and delete the pointer.}
- end;
- exit; {leave this procedure when all the}
- {references are deleted. }
- end;
-
- if Ndx_Tabl[Ndx_Lvl].Etry_No > Ndx_Tabl[Ndx_Lvl].Last_One then
- begin {if this was the last entry in the node,}
- {make sure non-leaf node pointers use }
- {the predecessor key value. }
- Ndx_Pntr := Addr(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary
- [Ndx_Tabl[Ndx_Lvl].Last_One]]);
- {point to the predecessor entry.}
- move(Ndx_Pntr^.Char_Fld,Ndx_Key_St[1],Ndx_Hdr.Key_Lgth);
- {Move the key field to Ndx_Key_St.}
- {The Move procedure must be used since}
- {Char_Fld is not a true Pascal string.}
- Ndx_Key_St[0] := chr(length(Work_Key));
- {Now insert the length into Ndx_Key_St}
- {so it is a valid string we can use}
- dec(Ndx_Lvl);
- if Ndx_Lvl > 0 then ReplacePointerEntry;
- {replace node pointer with this new key}
- end;
- end;
-
- procedure KeyDeleteStart;
- begin
- if Ndx_Tabl[Ndx_Lvl].Node_Pag then
- begin
- Ndx_Pntr := Addr(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary
- [Ndx_Tabl[Ndx_Lvl].Etry_No]]);
- RPag := Ndx_Pntr^.Block_Ax div Node_Size;
- {Get Next node number in RPag}
- GS_dBase_IX.Ndx_GetRecPage(false);
- Ndx_Tabl[Ndx_Lvl].Etry_No := Ndx_Tabl[Ndx_Lvl].Last_One +1;
- end;
- KeyDelete;
- end;
-
- { This routine will divide a block into two equal blocks and then store the
- index levels (n1 and n2), entry counts (e1 and e2), and block numbers
- (b1 and b2) for later node pointer updates. The new key (from the middle
- of the block's entries) will be saved in s1.
- }
- Procedure SplitBlock;
- begin
- b1 := GS_FileSize(Ndx_File) div Node_Size;
- {Get the next available block.}
- Ndx_NodeData(b1,1,Ndx_Tabl[Ndx_Lvl].Last_One,Ndx_Tabl[Ndx_Lvl].Node_Pag);
- {make a new index table entry}
- with Ndx_Tabl[Ndx_Lvl] do
- begin {put the first half of the block in the}
- {new block. Adjust the entry and last }
- {one counts accordingly. }
- n1 := Ndx_Lvl;
- Ndx_Data.Entry_Ct := Last_One div 2;
- {Number of entries in first half.}
- e2 := Last_One - Ndx_Data.Entry_Ct;
- {Number of entries in second half.}
- Last_One := Ndx_Data.Entry_Ct;
- e1 := Last_One;
- dec(Ndx_Data.Entry_Ct);
- Ndx_Pntr := Addr(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary
- [Ndx_Tabl[Ndx_Lvl].Last_One]]);
- move(Ndx_Pntr^.Char_Fld,s1[1],Ndx_Hdr.Key_Lgth);
- s1[0] := chr(Ndx_Hdr.Key_Lgth);
- pr := Ndx_Pntr^.Recrd_Ax;
- {Save the last key entry in the block.}
- Ndx_Put(Page_No); {Save the block.}
- end;
- dec(Ndx_Lvl);
- with Ndx_Tabl[Ndx_Lvl] do
- begin
- b2 := Page_No;
- n2 := Ndx_Lvl;
- Last_One := e2;
- Ndx_Data.Entry_Ct := e2;
- if Node_Pag then dec(Ndx_Data.Entry_Ct);
- move(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[succ(e1)]],
- Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[1]],
- Ndx_Hdr.Entry_Sz*(e2));
- {Shift second half to beginning of the}
- {buffer array.}
- Ndx_Put(Page_No); {Save the block}
- dec(Ndx_Lvl); {Step back to previous node.}
- end;
- end;
-
-
- { This routine is used to create a new root node when the split block
- pointers will not fit in the current root node.
- }
- Procedure MakeRootNode;
- var
- i : integer;
- begin
- Ndx_Lvl := 0;
- with Ndx_Tabl[Ndx_Lvl] do
- begin
- Page_No := GS_FileSize(Ndx_File) div Node_Size;
- {Get the next available block.}
- Ndx_Hdr.Root := Page_No * Node_Size;
- {Set root pointer to this block.}
- move(Ndx_Hdr, Ndx_Data, Node_Size);
- {Store from header info area}
- Ndx_Put(0); {Write updated header block.}
- FillChar(Ndx_Data, SizeOf(Ndx_Data),#0);
- for i := 0 to Ndx_Hdr.Max_Keys do Ndx_Data.Indx_Ary[succ(i)] :=
- ((Ndx_Hdr.Max_Keys + 2) * 2) + (Ndx_Hdr.Entry_Sz * i);
- Ndx_Pntr := Addr(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[1]]);
- Ndx_Data.Entry_Ct := 0;
- Ndx_Pntr^.Recrd_Ax := 0;
- Ndx_Pntr^.Block_Ax := b2 * Node_Size;
- Last_One := 1;
- Etry_No := 1;
- Ndx_Put(Page_No);
- end;
- end;
-
-
- { This routine will split the current node, create a new root node if needed,
- and then insert the newly created block in the proper sequence in the node.
- }
- procedure ExpandIndex;
- var
- kEOF : boolean;
- begin
- SplitBlock;
- if Ndx_Lvl = 0 then MakeRootNode;
- Work_Key := s1;
- Ndx_Get(Ndx_Tabl[Ndx_Lvl].Page_No);
- {Get the proper non-leaf node}
- kEOF := KeyEOF;
- KeyEOF := false; {temporarily turn off EOF flag}
- InsertEntry;
- KeyEOF := kEOF;
- Ndx_Pntr^.Recrd_Ax := pr;
- Ndx_Pntr^.Block_Ax := b1 * Node_Size;
- if Ndx_Tabl[Ndx_Lvl].Last_One <= Ndx_Hdr.Max_Keys then
- {test to see if more entries than the}
- {maximum allowed. }
- begin {write the block if below the max. }
- Ndx_Put(Ndx_Tabl[Ndx_Lvl].Page_No);
- end else
- ExpandIndex; {Keep expanding recursively as long as}
- {is necessary. }
- end;
-
-
- { This routine will insert the new key into the index. It will search for
- matching keys and insert the new key after any existing matches. It will
- then check to see if the node is filled, and split the block if necessary.
- }
- Procedure KeyInsert;
- begin
- nu_key := KeyFind(st); {Find a matching key.}
- if nu_key <> 0 then {If there is a match, continue looking}
- begin {until no more matches. }
- while (Ndx_Key_St = Work_Key) and (not KeyEOF) do
- nu_key := KeyRead(Next_Record);
- end;
- ForceInsert := Ndx_Tabl[Ndx_Lvl].Node_Pag;
- if ForceInsert then
- begin
- while Ndx_Tabl[Ndx_Lvl].Node_Pag do inc(Ndx_Lvl);
- Ndx_Get(Ndx_Tabl[Ndx_Lvl].Page_No);
- end;
- InsertEntry; {Insert the key here}
- Ndx_Pntr^.Recrd_Ax := rec;
-
- Ndx_Pntr^.Block_Ax := 0;
- if Ndx_Tabl[Ndx_Lvl].Last_One <= Ndx_Hdr.Max_Keys then
- {if fewer than the maximum number of key}
- {entries allowed, write the updated node}
- begin
- Ndx_Put(Ndx_Tabl[Ndx_Lvl].Page_No);
- end else
- begin
- ExpandIndex; {otherwise, split the block.}
- end;
- end;
-
- begin
- Work_Key := SetMatchValue(st); {Set key comparison value}
- if rec = crec then {Tests for Append vs Update}
- begin
- if KeyLocRec(rec) then
- begin
- if Work_Key = Ndx_Key_St then exit;
- KeyDelete;
- end;
- end;
- em_hold := ExactMatch;
- ExactMatch := true;
- KeyInsert;
- ExactMatch := em_hold;
- if crec < 0 then exit;
- lr := KeyFind(st);
- while lr <> rec do lr := KeyRead(Next_Record);
- end;
-
-
-
- Procedure GS_dBase_IX.KeyList(st : string);
- var
- ofil : text;
- RPag : LongInt;
- Lst_One,
- i,j,k,v : integer;
- rl : integer;
- ct : integer;
- prsz : integer;
- recnode,
- Less_Than : boolean;
- Fil_Siz : longint;
- begin
- assign(ofil, st);
- ReWrite(ofil);
- with Ndx_Hdr do
- fil_siz := GS_FileSize(Ndx_File) div Node_Size;
- with Ndx_Hdr do
- begin
- writeln(ofil,'--------------------------------------------------');
- writeln(ofil,'File Name = ',Ndx_Name);
- writeln(ofil,'Key Expression = ',Ndx_Key_Form);
- writeln(ofil,'Key Length = ',Key_Lgth,
- ' Maximum Keys/Block = ',Max_Keys);
- writeln(ofil,'Root =',Root div Node_Size:3,' Next Block Available:',
- fil_siz:3);
- end;
- RPag := 1;
- prsz := Ndx_Hdr.Key_Lgth;
- if prsz > 40 then prsz := 40;
- while RPag <> fil_siz do
- begin
- Ndx_Get(RPag);
- Lst_One := Ndx_Data.Entry_Ct+1;
- write(ofil,RPag:2,' [',Ndx_Data.Entry_Ct:3,']');
- Ndx_Pntr := Addr
- (Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[1]]);
- recnode := Ndx_Pntr^.Block_Ax = 0;
- k := Lst_One;
- if recnode then dec(k);
- v := 1;
- i := 1;
- while (i <= k) do
- begin
- Ndx_Pntr := Addr
- (Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[i]]);
- with Ndx_Pntr^ do
- begin
- write(ofil,'':v,Block_Ax div node_Size:5);
- v := 10;
- if i = Lst_One then write(ofil,' 0 - empty')
- else
- begin
- write(ofil,Recrd_Ax:5,' ');
- for j := 1 to prsz do
- write(ofil,Char_Fld[j]);
- end;
- WRITELN(OFIL);
- end;
- inc(i);
- end;
- writeln(ofil);
- inc(RPag);
- end;
- System.Close(ofil);
- end;
-
- end.
- {-----------------------------------------------------------------------------}
- END
-
-
-
-